/**** П Р И К Л А Д Н О Е  О П И С А Н И Е  A V R 1 3 4 **************
*
* Название: Часы Реального Времени
* Версия: 1.01
* Последнее обновление: 12.10.98
*
* ***** АДАПТИРОВАНО ДЛЯ C Компилятора CodeVisionAVR *****
* ***** HP InfoTech s.r.l. www.hpinfotech.ro *****
* ***** МОЖЕТ БЫТЬ ПРОТЕСТИРОВАНО КОМПЛЕКТОМ STK300 *****
*
* Цель: ATmega103 (Все AVR Устройства со вторым внешним генератором)
*
* Поддержка E-mail: avr@atmel.com
*
* Описание
* Это прикладное описание показывает, как осуществить Часы Реального 
* Времени, используя второй внешний генератор. Включена программа 
* проверки выполнения этой функции, которая хранит текущее 
* время, дату, месяц, и год с автоматической конфигурацией високосного 
* года. Чтобы отображать Часы Реального Времени, используется 8 
* светодиодов. 1-ый светодиод мигает каждую секунду, следующие шесть 
* представляют минуты, и 8-ой светодиод представляет час.
*********************************************************************/

// Определения регистров ввода/вывода для ATmega103
#include <mega103.h>

// Определения битов для регистра TIMSK
#define TOIE0 0
#define OCIE0 1
// Определения битов для регистра ASSR
#define AS0 3

char not_leap(void);

typedef struct
{
unsigned char second; //ввод текущего времени, даты, месяца и года
unsigned char minute;
unsigned char hour;
unsigned char date;
unsigned char month;
unsigned int year;
}time;

time t;

void main(void)
{
// инициализация часов реального времени
int temp0,temp1;

for(temp0=0;temp0<0x0040;temp0++) // Ожидание, чтобы 
                                  // стабилизировался внешний 
                                  // тактовый генератор
{
for(temp1=0;temp1<0xFFFF;temp1++);
}
DDRB=0xFF;
TIMSK &=~((1<<TOIE0)|(1<<OCIE0)); // Запретим прерывание TC0,
ASSR |= (1<<AS0);                 // установим Таймер/Счетчик 0 
                                  // асинхронным с тактовой
// частотой CPU. Им управляет
// второй внешний тактовый
// генератор(32,768kHz)
TCNT0 = 0x00;
TCCR0 = 0x05; // к-т деления таймера - исходная частота / 128,
// чтобы каждое переполнение происходило точно через
// 1 секунду
while(ASSR&0x07);    // Ждем, пока TC0 не обновится
TIMSK |= (1<<TOIE0); // устанавливаем Разрешение Прерывания по
// Переполнению 8-битового Таймера/Счетчика 0
#asm("sei")   // устанавливаем Бит Глобального Разрешения Прерывания

while(1)
{
MCUCR = 0x38;  // введем спящий режим: энергосберегающий режим
#asm("sleep")  // пробуждение во время прерывания по переполнению
#asm("nop")
  OCR0 = 0; // Запишем фиктивное значение в Выходной регистр Сравнения                    
while(ASSR&0x02); // Ждем, пока OCR0 не обновится
  }
}

interrupt [TIM0_OVF] void counter(void) // вектор прерывания по 
                                        // переполнению 
{
  if (++t.second==60)      // отслеживаем время, дату, месяц и год
  {
    t.second=0;
    if (++t.minute==60)
    {
      t.minute=0;
      if (++t.hour==24)
      {
        t.hour=0;
        if (++t.date==32)
        {
          t.month++;
          t.date=1;
        }
        else if (t.date==31)
        {
          if ((t.month==4)||(t.month==6)||(t.month==9)||(t.month==11))
          {
            t.month++;
            t.date=1;
          }
        }
        else if (t.date==30)
        {
          if(t.month==2)
          {
            t.month++;
            t.date=1;
          }
        }
        else if (t.date==29)
        {
          if((t.month==2) && (not_leap()))
          {
            t.month++;
            t.date=1;
          }
        }
        if (t.month==13)
        {
          t.month=1;
          t.year++;
        }
      }
    }
  }
  PORTB=~(((t.second&0x01)|t.minute<<1)|t.hour<<7);

}

char not_leap(void)          // изменения для високосного года
{
  if (!(t.year%100))
    return (char)(t.year%400);
  else
    return (char)(t.year%4);
}
